#if DLIF_ES3
using System;

namespace GameHelper.Saving
{
    using UnityEngine;
    using UnityEngine.Assertions;

    // TODO: 添加数值混淆，防止内存修改

    /// <summary>
    ///   存档适配器: Easy Save3实现
    /// </summary>
    public partial class SaveAdapter
    {
        static bool _typesInited;

        ES3File _esFile;

        public ES3File esFile => _esFile;
        // 文件已删除
        bool _deleted;
        // 禁止类型错误时抛出异常
        bool _suppressTypeError;

        private static void InitTypes()
        {
            if (_typesInited)
                return;
            ES3.Init();
            _typesInited = true;
        }

        /// <summary>
        ///   存档构造
        /// </summary>
        /// <param name="path">存档路径</param>
        /// <param name="password">AES密码, 密码为32个字符</param>
        /// <param name="suppressTypeError">禁止类型错误时抛出异常</param>
        public SaveAdapter(string path, string password, bool suppressTypeError)
        {
            InitTypes();
            Assert.IsTrue(!string.IsNullOrEmpty(password) && password.Length == 32, "密码必须是32个字符长度");
            _suppressTypeError = suppressTypeError;
            ES3.EncryptionType encryptType = ES3.EncryptionType.AES;
#if UNITY_EDITOR
            // Editor模式下明文，简化调试流程
            encryptType = ES3.EncryptionType.None;
            password = null;
#endif
            ES3Settings settings = new ES3Settings(path, encryptType, password);
#if UNITY_EDITOR
            // Editor模式下格式化存档输出
            settings.prettyPrint = true;
#else
            settings.prettyPrint = false;
#endif
            try
            {
                _esFile = new ES3File(settings, true);
            }
            catch
            {
                Debug.LogWarningFormat("Load sav failed.");
                // 存档加载失败：可能是被篡改或者文件损坏
                ES3.DeleteFile(settings);
                _esFile = new ES3File(settings, true);
            }
        }

        /// <summary>
        ///   存档写入文件
        /// </summary>
        public void Flush()
        {
            if (!_deleted)
            {
                _esFile.Sync();
            }
        }

        #region 范型接口

        /// <summary>
        ///   存档写入
        /// </summary>
        public void Set<T>(string key, T value)
        {
            _esFile.Save<T>(key, value);
        }

        /// <summary>
        /// 存档读取，如果键值不存在或者类型错误，返回fallback
        /// </summary>
        public T Get<T>(string key, T fallback)
        {
            try
            {
                return _esFile.Load<T>(key, fallback);
            }
            catch (System.Exception e)
            {
                if (!_suppressTypeError)
                {
                    throw e;
                }
                else
                {
                    return fallback;
                }
            }
        }

        /// <summary>
        /// 存档安全读取，如果读取成功，返回true，value为读取到的结果
        /// </summary>
        /// <returns>读取失败返回false（键不存在，类型错误）</returns>
        public bool TryGet<T>(string key, out T value)
        {
            try
            {
                value = _esFile.Load<T>(key);
                return true;
            }
            catch (System.Collections.Generic.KeyNotFoundException)
            {
            }
            catch (System.Exception e)
            {
                if (!_suppressTypeError)
                {
                    throw e;
                }
            }
            value = default(T);
            return false;
        }

        /// <summary>
        ///   存档读取，键值不存在或者类型错误(suppressTypeError = false)时会抛出异常, 慎用!!!
        /// </summary>
        public T Get<T>(string key)
        {
            try
            {
                return _esFile.Load<T>(key);
            }
            catch (System.Collections.Generic.KeyNotFoundException e)
            {
                throw e;
            }
            catch (System.Exception e)
            {
                if (!_suppressTypeError)
                {
                    throw e;
                }
                else
                {
                    return default(T);
                }
            }
        }

        #endregion 范型接口

        /// <summary>
        ///   移除存档
        /// </summary>
        public void Remove(string key)
        {
            _esFile.DeleteKey(key);
        }

        /// <summary>
        /// 获取底层的Key Type，一般底层都使用string类型作Key, 为了简化接口，添加范型接口
        /// </summary>
        /// <param name="key"></param>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        string GetInternalKey<T>(T key)
        {
            var t = typeof(T);
            if (t.IsValueType)
            {
                if (t.IsEnum)
                {
                    throw new Exception($"Enum key not support! [{t}]");
                }
                var typeCode = Type.GetTypeCode(t);
                switch (typeCode)
                {
                    case TypeCode.Int16:
                    case TypeCode.UInt16:
                    case TypeCode.Int32:
                    case TypeCode.UInt32:
                    case TypeCode.Int64:
                    case TypeCode.UInt64:
                        {
                            return key.ToString();
                        }
                    default:
                        {
                            throw new Exception($"Invalid key type! [{t}]");
                        }
                }
            }
            else if (t == typeof(string))
            {
                return key as string;
            }
            else
            {
                throw new Exception($"Unsupport key type! [{t}]");
            }
        }

        /// <summary>
        /// 移除存档范型接口
        /// </summary>
        /// <param name="key"></param>
        /// <typeparam name="T">int/uint, long/ulong, short/ushort, string, Enum</typeparam>
        public void Remove<T>(T key)
        {
            Remove(GetInternalKey<T>(key));
        }

        /// <summary>
        ///   检测存档是否存在
        ///   !! 注意: api只能判断键值是否存在，不能判断类型是否正确
        /// </summary>
        public bool HasKey(string key)
        {
            return _esFile.KeyExists(key);
        }

        /// <summary>
        /// 检测存档键值是否存在
        /// !! 注意: api只能判断键值是否存在，不能判断类型是否正确
        /// </summary>
        /// <param name="key"></param>
        /// <typeparam name="T">int/uint, long/ulong, short/ushort, string, Enum</typeparam>
        /// <returns></returns>
        public bool HasKey<T>(T key)
        {
            return HasKey(GetInternalKey<T>(key));
        }

        /// <summary>
        ///   清空所有存档
        /// </summary>
        public void Clear()
        {
            _esFile.Clear();
        }

        /// <summary>
        /// 删除存档文件
        /// </summary>
        public void DeleteFile()
        {
            _deleted = true;
            _esFile.Clear();
            ES3.DeleteFile(_esFile.settings);
        }
    }
}

#endif
